#ifndef LIST_HPP
#define LIST_HPP

template <typename T>
class List {
public:
    enum ErrorNumber {
        Error_None,
        Error_MallocFailed,
        Error_FreeFailed,
        Error_IllegalIndex,
    };
private:
    struct Item {
        T value;
        Item *next;
    };
public:
    List(){
        this->header = nullptr;
        this->size = 0;
        this->error_number = Error_None;
    }

    bool append(T value){
        Item **curr = &this->header;
        while ( *curr != nullptr ){
            curr = &((*curr)->next);
        }
        
        (*curr) = new Item;
    
        if ( *curr == nullptr ){
            this->error_number = Error_MallocFailed;
            return false;
        }
        (*curr)->value = value;
        (*curr)->next = nullptr;
        this->size += 1;
        this->error_number = Error_None;
        return true;
    }

    bool insert(T value,int pos){
        if ( pos > this->size ){
            this->error_number = Error_IllegalIndex;
            return false;
        }
        Item **curr = &this->header;
        int curr_index = 0;
        while ( *curr != nullptr && curr_index < pos ){
            curr = &((*curr)->next);
            curr_index += 1;
        }
        Item *new_item = nullptr;
        
        new_item = new Item;
        
        if ( new_item == nullptr ){
            this->error_number = Error_MallocFailed;
            return false;
        }
        new_item->value = value;
        new_item->next = (*curr);
        (*curr) = new_item;
        this->size += 1;
        this->error_number = Error_None;
        return true;
    }

    bool remove(int pos){
        int curr_pos = -1;
        Item **curr = &this->header;
        Item **prev = &this->header;
        while ( curr_pos != pos && *curr != nullptr ){
            prev = curr;
            curr = &((*curr)->next);
            curr_pos += 1;
        }
        if ( *prev == nullptr ){
            this->error_number = Error_IllegalIndex;
            return false;
        }
        Item *temp = (*curr);
        delete (*prev);
        (*prev) = temp;
        this->size -= 1;
        this->error_number = Error_None;
        return true;
    }

    bool set(T value,int pos){
        int curr_pos = 0;
        Item **curr = &this->header;
        while ( curr_pos != pos && *curr != nullptr ){
            curr = &((*curr)->next);
            curr_pos += 1;
        }
        if ( *curr == nullptr ){
            this->error_number = Error_IllegalIndex;
            return false;
        }
        (*curr)->value = value;
        this->error_number = Error_None;
        return true;
    }

    T get(int pos){
        T ret;
        int curr_pos = 0;
        Item **curr = &this->header;
        for ( int nbyte = 0; nbyte < sizeof(T); nbyte++ ){
            ((unsigned char*)&ret)[nbyte] = 0;
        }
        while ( curr_pos != pos && *curr != nullptr ){
            curr = &((*curr)->next);
            curr_pos += 1;
        }
        if ( *curr == nullptr ){
            this->error_number = Error_IllegalIndex;
            return ret;
        }
        this->error_number = Error_None;
        return (*curr)->value;
    }

    bool clear(void){
        while ( this->getSize() > 0 ){
            if ( !this->remove(0) ){
                return false;
            }
        }
        return true;
    }

    int getSize(void){
        return this->size;
    }

    int getErrorNumber(void){
        return this->error_number;
    }

private:
    Item *header;
    int size;
    int error_number;
};

#endif
